﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XNAViewerSample;
using ConfigReader;

namespace XNAViewerSample.Physics
{
    public class SoftBodyDynamics
    {
        #region Fields
        protected List<SoftBodyObjects> sbObjects = new List<SoftBodyObjects>();
        protected List<ForceGenerators> globalForceGenerators = new List<ForceGenerators>();
        protected List<Spring> springs = new List<Spring>();
        protected Integrators integrator;
        protected List<Constraints> constraints = new List<Constraints>();
        protected int constraintIterations;
        protected CSkinnedModel model;
        protected ConfigReader.Physics.PType type;
        protected bool isEnabled;

        public List<SoftBodyObjects> SoftBodyObjects
        {
            get { return sbObjects; }
            set { sbObjects = value; }
        }
        public Integrators Integrator
        {
            get { return integrator; }
            set { integrator = value; }
        }
        public List<Constraints> Constraints
        {
            get { return constraints; }
            set { constraints = value; }
        }
        public int ConstraintIterations
        {
            get { return constraintIterations; }
            set { constraintIterations = value; }
        }
        public ConfigReader.Physics.PType Type
        {
            get { return type; }
            set { type = value; }
        }
        public List<ForceGenerators> GlobalForceGenerators
        {
            get { return globalForceGenerators; }
        }
        public Boolean IsEnabled
        {
            get { return isEnabled; }
            set { isEnabled = value; }
        }
        #endregion
        #region Constructor
        public SoftBodyDynamics(CSkinnedModel model)
        {
            this.model = model;
            this.constraintIterations = 30;

            //create a default integrator
            this.integrator = new ForwardEulerIntegrator();
            this.isEnabled = true;
        }
        #endregion

        #region Methods
        public void AddSoftBodyObject(SoftBodyObjects sbObject)
        {
            sbObjects.Add(sbObject);
        }

        public void AddSpring(float stiffness, float damping, SoftBodyObjects sbObjA, SoftBodyObjects sbObjB)
        {
            Spring spring = new Spring(stiffness, damping, sbObjA, sbObjB);
            springs.Add(spring);
        }


        public void AddGlobalForceGenerator(ForceGenerators forceGenerator)
        {
            globalForceGenerators.Add(forceGenerator);
        }

        Vector3 acceleration;
        public virtual void Update(GameTime gameTime)
        {
            //sum all local forces
            foreach (Spring spring in springs)
            {
                spring.ApplyForce(null);  //no need to specify any simObj
            }

            //sum all global forces acting on the objects
            foreach (SoftBodyObjects sbObject in sbObjects)
            {
                if (sbObject.SBObjectType == SBObjectType.ACTIVE)
                {
                    foreach (ForceGenerators forceGenerator in globalForceGenerators)
                    {
                        forceGenerator.ApplyForce(sbObject);
                    }
                }
            }

            foreach (SoftBodyObjects sbObject in sbObjects)
            {
                if (sbObject.SBObjectType == SBObjectType.ACTIVE)
                {
                    //find acceleration
                    acceleration = sbObject.ResultantForce / sbObject.Mass;

                    //integrate
                    integrator.Integrate(acceleration, sbObject, gameTime);
                }
            }

            //satisfy constraints
            for (int i = 0; i < constraintIterations; i++)
            {
                foreach (Constraints constraint in constraints)
                {
                    constraint.SatisfyConstraint();
                }
            }

            //update object
            //foreach (SoftBodyObjects sbObject in sbObjects)
            //{
            //    sbObject.Update(gameTime);
            //}

            //reset forces on sim objects
            foreach (SoftBodyObjects sbObject in sbObjects)
            {
                if (sbObject.SBObjectType == SBObjectType.ACTIVE)
                {
                    sbObject.ResetForces();
                }
            }
        }
        #endregion
    }
}
